/**
 * 方盒水动效果 v5 - 完整版
 * 硬件：DFRobot Beetle ESP32-C3 (DFR0868)
 *       LIS2DW12 三轴加速度计 (SEN0405)
 *       8x8 RGB LED Matrix (DFR0459)
 * 
 * 核心特性：
 *   - 水永远堆在重力方向指向的点阵一侧（密封盒子效果）
 *   - 完全倾倒 90° 时，水完全倒向一侧
 *   - 剧烈晃动触发淡蓝色水花
 *   - 支持 WS2812 蛇形布局矩阵
 * 
 * 传感器坐标系（基于实测）：
 *   X轴 = 点阵的"外"方向（朝用户）
 *   Y轴 = 点阵的"右"方向
 *   Z轴 = 点阵的"上"方向
 */

#include <Adafruit_NeoPixel.h>
#include <DFRobot_LIS2DW12.h>
#include <Wire.h>

// ==================== 引脚定义 ====================
#define LED_PIN       8
#define I2C_SDA_PIN   4
#define I2C_SCL_PIN   1

// ==================== 矩阵布局配置 ====================
// 大部分 WS2812 8x8 矩阵是蛇形布局，设为 1
// 如果你的矩阵是逐行布局，改为 0
#define SERPENTINE_LAYOUT 1

// ==================== 传感器方向校准 ====================
// 如果发现水流向错误的一侧，翻转对应符号
#define SENSOR_Y_SIGN   1   // 点阵左右方向（-1 翻转左右）
#define SENSOR_Z_SIGN   1   // 点阵上下方向（-1 翻转上下）

// ==================== 点阵尺寸 ====================
#define MATRIX_W   8
#define MATRIX_H   8
#define NUM_LEDS   (MATRIX_W * MATRIX_H)

// ==================== 对象 ====================
DFRobot_LIS2DW12_I2C acce(&Wire, 0x19);
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

// ==================== 状态变量 ====================
// 重力方向在点阵平面上的单位向量（即水堆积的中心方向）
float gDirY =  0.0;   // + 表示重力朝点阵右侧
float gDirZ = -1.0;   // -1 表示重力朝点阵下方（竖直放置时的默认状态）

float splashIntensity = 0.0;

// ==================== 可调参数 ====================
const float DIR_SMOOTH       = 0.25;   // 方向平滑（越大响应越快）
const float SPLASH_DECAY     = 0.88;   // 水花衰减速度
const int   SPLASH_THRESHOLD = 350;    // 水花触发阈值 (mg)
const float SURFACE_WIDTH    = 0.12;   // 水面线宽度（投影值）

// ============================================================
//  初始化
// ============================================================
void setup() {
  Serial.begin(115200);
  delay(500);
  Serial.println("\n=== 方盒水动效果 v5 ===");

  // ---- 初始化 LED 点阵 ----
  strip.begin();
  strip.setBrightness(80);
  strip.clear();
  strip.show();
  Serial.println("LED 点阵初始化完成");

  // ---- 初始化 I2C ----
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
  Serial.printf("I2C 初始化完成 (SDA=%d, SCL=%d)\n", I2C_SDA_PIN, I2C_SCL_PIN);

  // ---- 初始化加速度计 ----
  while (!acce.begin()) {
    Serial.println("传感器初始化失败，请检查接线...");
    delay(1000);
  }
  Serial.printf("传感器 ID: 0x%X\n", acce.getID());

  acce.softReset();
  acce.setRange(DFRobot_LIS2DW12::e2_g);
  acce.setDataRate(DFRobot_LIS2DW12::eRate_50hz);
  acce.setPowerMode(DFRobot_LIS2DW12::eContLowPwrLowNoise2_14bit);
  acce.continRefresh(true);
  acce.setFilterPath(DFRobot_LIS2DW12::eLPF);

  Serial.println("传感器初始化完成");
  Serial.println("================================\n");
}

// ============================================================
//  主循环
// ============================================================
void loop() {
  // ---------- 1. 读取三轴加速度 ----------
  int32_t ax = acce.readAccX();
  int32_t ay = acce.readAccY();
  int32_t az = acce.readAccZ();

  // ---------- 2. 计算重力方向 ----------
  // 静止时加速度读数 = 比力方向（世界上方）
  // 重力方向（世界下方）= -加速度方向（归一化）
  float A = sqrt((float)ax * ax + (float)ay * ay + (float)az * az);
  if (A < 1.0) A = 1.0;

  float gy = SENSOR_Y_SIGN * (-ay / A);
  float gz = SENSOR_Z_SIGN * (-az / A);

  // 取 Y-Z 平面上的投影
  float projSize = sqrt(gy * gy + gz * gz);

  float targetDirY, targetDirZ;
  if (projSize < 0.12) {
    // 点阵平面几乎水平（朝上或朝下）
    // 此时重力垂直于屏幕，无法在平面内表现，默认水在下方
    targetDirY = 0.0;
    targetDirZ = -1.0;
  } else {
    targetDirY = gy / projSize;
    targetDirZ = gz / projSize;
  }

  // 平滑方向（避免抖动导致水乱跳）
  gDirY += (targetDirY - gDirY) * DIR_SMOOTH;
  gDirZ += (targetDirZ - gDirZ) * DIR_SMOOTH;

  // 重新归一化
  float dLen = sqrt(gDirY * gDirY + gDirZ * gDirZ);
  if (dLen < 0.001) dLen = 1.0;
  gDirY /= dLen;
  gDirZ /= dLen;

  // ---------- 3. 水花检测 ----------
  static int32_t lastX = 0, lastY = 0, lastZ = 0;
  int32_t dX = abs(ax - lastX);
  int32_t dY = abs(ay - lastY);
  int32_t dZ = abs(az - lastZ);
  lastX = ax; lastY = ay; lastZ = az;

  if (dX > SPLASH_THRESHOLD || dY > SPLASH_THRESHOLD || dZ > SPLASH_THRESHOLD) {
    splashIntensity = 1.0;
    Serial.printf("水花！ΔX=%d ΔY=%d ΔZ=%d\n", dX, dY, dZ);
  }
  splashIntensity *= SPLASH_DECAY;
  if (splashIntensity < 0.02) splashIntensity = 0.0;

  // ---------- 4. 渲染点阵 ----------
  strip.clear();

  for (int row = 0; row < MATRIX_H; row++) {
    for (int logicalCol = 0; logicalCol < MATRIX_W; logicalCol++) {

      // 逻辑坐标（屏幕坐标）
      float px = (logicalCol - 3.5) / 3.5;   // -1 左 ~ +1 右
      float py = (row - 3.5) / 3.5;          // -1 上 ~ +1 下

      // 沿重力方向的投影
      // 投影值大 = 靠近"水堆积中心" = 水体深处
      // 投影值小或负 = 远离水堆积中心 = 空气
      float proj = px * gDirY + py * (-gDirZ);

      uint32_t color = 0;

      if (proj > SURFACE_WIDTH) {
        // ---- 水体 ----
        float depthRatio = (proj - SURFACE_WIDTH) / (1.0 - SURFACE_WIDTH);
        depthRatio = constrain(depthRatio, 0.0, 1.0);
        int g = 20 + (int)(depthRatio * 60);
        int b = 180 + (int)(depthRatio * 75);
        color = strip.Color(0, g, b);

      } else if (proj > -SURFACE_WIDTH) {
        // ---- 水面线 ----
        color = strip.Color(0, 120, 255);

      } else if (splashIntensity > 0.1) {
        // ---- 水花：淡蓝色，离水面越远越淡 ----
        float dist = -proj - SURFACE_WIDTH;   // 距水面的距离
        float splashProb = splashIntensity * (1.0 - dist * 2.0);
        splashProb = constrain(splashProb, 0.0, 1.0);

        if (random(1000) < splashProb * 450) {
          // 距离越远亮度越低
          float fade = 1.0 - dist * 0.5;
          fade = constrain(fade, 0.25, 1.0);

          int brightness = (180 + random(75)) * fade;  // 基础亮度 + 随机
          int r = brightness * 0.30;                   // 30% 红
          int g = brightness * 0.70;                   // 70% 绿
          int b = brightness;                          // 100% 蓝
          color = strip.Color(r, g, b);
        }
      }

      // ★ 根据矩阵布局确定物理像素列（蛇形布局适配）★
      int physicalCol;
#if SERPENTINE_LAYOUT
      physicalCol = (row % 2 == 0) ? logicalCol : (MATRIX_W - 1 - logicalCol);
#else
      physicalCol = logicalCol;
#endif

      if (color != 0) {
        int pixelIndex = row * MATRIX_W + physicalCol;
        strip.setPixelColor(pixelIndex, color);
      }
    }
  }
  strip.show();

  // ---------- 5. 串口调试输出 ----------
  static unsigned long lastPrint = 0;
  if (millis() - lastPrint > 300) {
    lastPrint = millis();
    Serial.printf("X=%5d Y=%5d Z=%5d | 重力方向=(%+.2f,%+.2f) | splash=%.2f\n",
                  ax, ay, az, gDirY, gDirZ, splashIntensity);
  }

  delay(20);  // 约 50fps 刷新率
}